package hh.engine;

import hh.Column;
import hh.util.CommonUtil;
import hh.util.IoUtil;
import hh.util.StringUtil;
import hh.util.bean.BeanUtil;
import hh.util.jdbc.DbProperties;
import hh.util.jdbc.JdbcUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.util.*;

@Data
@Slf4j
@Accessors(chain = true)
public class Context implements Closeable {

    String dbSettingPath;
    DbProperties dbProperties;
    Connection connection;
    List<Table> tables;
    Map<String, Class> typeMapping;
    Engine engine;

    public static Context create(String path) {
        File file = new File(path);
        if (!file.exists()) file = new File(CommonUtil.getClassPath(Context.class, path));
        if (!file.exists()) throw new RuntimeException(path + " 不存在");

        Context context = new Context();
        context.setDbSettingPath(file.getAbsolutePath());
        context.load(file);
        return context;
    }

    public void load(File file) {

        this.dbSettingPath = file.getAbsolutePath();
        Properties properties = IoUtil.loadProperties(file.getAbsolutePath());
        log.info("加载数据库配置文件：{}", properties);
        this.dbProperties = BeanUtil.mapToBean(properties, DbProperties.class);
        this.connection = JdbcUtil.getConnection(dbProperties.getUrl(), dbProperties.getUsername(), dbProperties.getPassword());
        log.info("加载数据库连接完成");

        String tables = (String) properties.get("tables");
        log.info("准备加载tables：{}", tables);

        setTypeMapping((String) properties.get("typeMapping"));
        if (StringUtil.isNotEmpty(tables)) {
            this.tables = parseTable(tables);
        }
    }

    private void setTypeMapping(String typeMapping) {
        if (StringUtil.isEmpty(typeMapping)) return;

        Map<String, Class> map = new HashMap<>();
        for (String s : typeMapping.split(",")) {
            String[] split = s.split(":");
            Class<?> value = JdbcUtil.typeAlias.get(split[1].toLowerCase());
            if ((value == null)) {
                throw new RuntimeException("未知类型：" + split[0]);
            }
            map.put(split[0], value);
        }
        this.typeMapping = map;
    }

    private List<Table> parseTable(String tbString) {
        String[] split = tbString.split(",");
        String schemaSql = StringUtil.isEmpty(dbProperties.getDbName()) ? "" : "and table_schema=" + JdbcUtil.quoteValue(dbProperties.getDbName());

        List<Table> tbs = new ArrayList<>();
        for (String tableName : split) {
            String simpleTableName = StringUtil.getSimpleTableName(tableName);

            Object tableComment = JdbcUtil.selectOneRowOneColumn(connection,
                    "select * from information_schema.tables where table_name = ?" + schemaSql,
                    JdbcUtil.quoteValue(simpleTableName));
            Table tb = new Table(simpleTableName, (String) tableComment);
            List<Map<String, Object>> data = JdbcUtil.listMap(connection, "show full columns from " + tableName);
            for (Map<String, Object> map : data) {
                String field = (String) map.get("Field");
                String type = (String) map.get("Type");
                String comment = (String) map.get("Comment");
                Column column = new Column(field, type, comment, getJavaType(type));
                tb.addColumn(column);
            }
            tbs.add(tb);
        }
        return tbs;
    }

    private Class<?> getJavaType(String type) {
        int i = type.indexOf("(");
        if(i!=-1) type = type.substring(0, i);
        Class aClass = typeMapping.get(type.toLowerCase());
        if (aClass == null) aClass = JdbcUtil.typeMapping.get(type.toLowerCase());
        return aClass;
    }


    @Override
    public void close() throws IOException {
        CommonUtil.closeQuietly(connection);
    }
}
